home *** CD-ROM | disk | FTP | other *** search
/ PC World Komputer 2010 April / PCWorld0410.iso / hity wydania / Ubuntu 9.10 PL / karmelkowy-koliberek-9.10-netbook-remix-PL.iso / casper / filesystem.squashfs / usr / share / pyshared / nevow / rend.py < prev    next >
Text File  |  2008-01-03  |  30KB  |  821 lines

  1. # Copyright (c) 2004 Divmod.
  2. # See LICENSE for details.
  3.  
  4. """Page, Fragment and other standard renderers.
  5.  
  6. This module contains classes and function responsible for rendering
  7. dynamic content and a few useful mixin classes for inheriting common
  8. functionality.
  9.  
  10. Mostly, you'll use the renderers:
  11.  
  12.  - B{Page} - Nevow's main resource type for rendering web pages and
  13.    locating child resource.
  14.  - B{Fragment} - useful for rendering more complex parts of a document
  15.    that require a set of data_* and render_* methods.
  16.  - B{sequence} - render each item in a sequence.
  17.  - B{mapping} - publish a dictionary by filling slots
  18. """
  19.  
  20. from time import time as now
  21. from cStringIO import StringIO
  22. import random
  23. import warnings
  24.  
  25. from zope.interface import implements, providedBy
  26.  
  27. import twisted.python.components as tpc
  28. from twisted.python.reflect import qual, accumulateClassList
  29.  
  30. from nevow.context import WovenContext, NodeNotFound, PageContext
  31. from nevow import inevow, tags, flat, util, url
  32. from nevow.util import log
  33.  
  34. import formless
  35. from formless import iformless, annotate
  36.  
  37.  
  38. def _getPreprocessors(inst):
  39.     """
  40.     Accumulate elements from the sequences bound at the C{preprocessors}
  41.     attribute on all classes in the inheritence hierarchy of the class of
  42.     C{inst}.  A C{preprocessors} attribute on the given instance overrides
  43.     all preprocessors from the class inheritance hierarchy.
  44.     """
  45.     if 'preprocessors' in vars(inst):
  46.         return inst.preprocessors
  47.     preprocessors = []
  48.     accumulateClassList(
  49.         inst.__class__,
  50.         'preprocessors',
  51.         preprocessors)
  52.     return preprocessors
  53.  
  54.  
  55.  
  56. class RenderFactory(object):
  57.     implements(inevow.IRendererFactory)
  58.  
  59.     def renderer(self, context, name):
  60.         """Return a renderer with the given name.
  61.         """
  62.  
  63.         # The named renderer can be parameterised, i.e. 'renderIt one,two,three'
  64.         args = []
  65.         if name.find(' ') != -1:
  66.             name, args = name.split(None, 1)
  67.             args = [arg.strip() for arg in args.split(',')]
  68.  
  69.         callable = getattr(self, 'render_%s' % name, None)
  70.         if callable is None:
  71.             warnings.warn(
  72.                 "Renderer %r missing on %s will result in an exception." % (
  73.                     name, qual(type(self))),
  74.                 category=DeprecationWarning,
  75.                 stacklevel=1)
  76.             callable = lambda *a, **kw: context.tag[
  77.                 "The renderer named '%s' was not found in %r." % (name, self)]
  78.  
  79.         if args:
  80.             return callable(*args)
  81.  
  82.         return callable
  83.  
  84.     render_sequence = lambda self, context, data: sequence(context, data)
  85.     render_mapping = lambda self, context, data: mapping(context, data)
  86.     render_string = lambda self, context, data: string(context, data)
  87.     render_xml = lambda self, context, data: context.tag.clear()[tags.xml(data)]
  88.     render_data = lambda self, context, data_: data(context, data_)
  89.  
  90.  
  91. class MacroFactory(object):
  92.     implements(inevow.IMacroFactory)
  93.  
  94.     def macro(self, ctx, name):
  95.         """Return a macro with the given name.
  96.         """
  97.         # The named macro can be parameterized, i.e. 'macroFoo foo,bar,baz'
  98.         args = []
  99.         if name.find(' ') != -1:
  100.             name, args = name.split(None, 1)
  101.             args = [arg.strip() for arg in args.split(',')]
  102.  
  103.         callable = getattr(self, 'macro_%s' % name, None)
  104.         if callable is None:
  105.             callable = lambda ctx, *args: ctx.tag[
  106.                 "The macro named '%s' was not found in %r." % (name, self)]
  107.  
  108.         if args:
  109.             ## Macros are expanded in TagSerializer by calling them with a single arg, the context
  110.             return lambda ctx: callable(ctx, *args)
  111.  
  112.         return callable
  113.  
  114.  
  115. class DataNotFoundError(Exception):
  116.     """Raised when a data directive could not be resolved on the page or its
  117.     original attribute by the DataFactory.
  118.     """
  119.  
  120.  
  121. class DataFactory(object):
  122.     implements(inevow.IContainer)
  123.  
  124.     def child(self, context, n):
  125.         args = []
  126.         if n.find(' ') != -1:
  127.             name, args = n.split(None, 1)
  128.             args = [arg.strip() for arg in args.split(',')]
  129.         else:
  130.             name = n
  131.  
  132.         callable = getattr(self, 'data_%s' % name, None)
  133.         ## If this page doesn't have an appropriate data_* method...
  134.         if callable is None:
  135.             ## See if our self.original has an IContainer...
  136.             container = inevow.IContainer(self.original, None)
  137.             if container is None:
  138.                 raise DataNotFoundError("The data named %r was not found in %r." % (name, self))
  139.             else:
  140.                 ## And delegate to it if so.
  141.                 return container.child(context, n)
  142.  
  143.         if args:
  144.             return callable(*args)
  145.  
  146.         return callable
  147.  
  148.  
  149. class FreeformChildMixin:
  150.     """Mixin that handles locateChild for freeform segments."""
  151.     def locateChild(self, ctx, segments):
  152.         request = inevow.IRequest(ctx)
  153.         ## The method or property name we are going to validate against/affect
  154.         bindingName = None
  155.  
  156.         name = segments[0]
  157.         if name.startswith('freeform_post!'):
  158.             configurableName, bindingName = name.split('!')[1:3]
  159.         elif name.startswith('freeform-action-post!'):
  160.             configurableName, request.args['freeform-actee'] = name.split('!')[1:3]
  161.             bindingName = request.args['freeform-action'][0]
  162.         if bindingName:
  163.             ctx.remember(self, inevow.IResource)
  164.             ctx.remember(request, inevow.IRequest)
  165.             cf = iformless.IConfigurableFactory(self)
  166.             def checkC(c):
  167.                 if c is not None:
  168.                     return self.webFormPost(request, self, c, ctx, bindingName, request.args)
  169.             return util.maybeDeferred(cf.locateConfigurable, ctx, configurableName).addCallback(checkC)
  170.         return NotFound
  171.  
  172.     def child_freeform_hand(self, ctx):
  173.         carryoverHand = inevow.IHand(ctx, None)
  174.         if carryoverHand is not None:
  175.             inevow.ISession(ctx).setComponent(inevow.IHand, carryoverHand)
  176.             return carryoverHand
  177.         return inevow.IHand(inevow.ISession(ctx), None)
  178.  
  179.  
  180. class ConfigurableMixin(object):
  181.     """A sane IConfigurable implementation for Fragment and Page.
  182.     Methods starting with bind_ automatically expose corresponding
  183.     method names. bind_* should return an IBinding (PropertyBinding
  184.     or MethodBinding), or, as a shortcut for MethodBinding, a list of
  185.     twoples like this:
  186.  
  187.     def bind_foo(self, ctx):
  188.         return [('argName', String()), ('anotherArg', Integer())]
  189.  
  190.     def foo(self, argName, anotherArg):
  191.         assert isinstance(argName, str)
  192.         assert isinstance(anotherArg, int)
  193.  
  194.     """
  195.     implements(iformless.IConfigurable)
  196.  
  197.     def getBindingNames(self, ctx):
  198.         """Expose bind_* methods and attributes on this class.
  199.         """
  200.         for name in dir(self):
  201.             if name.startswith('bind_'):
  202.                 yield name[len('bind_'):]
  203.  
  204.     def getBinding(self, ctx, name):
  205.         """Massage bind_* methods and attributes into an
  206.         IBinding. The bind_* method or attribute can either
  207.         already implement IBinding or be a list of twoples
  208.         which will be massaged into a MethodBinding as
  209.         described in the ConfigurableMixin class docstring.
  210.         """
  211.         def _get_binding(binding):
  212.             if callable(binding):
  213.                 binding = util.maybeDeferred(binding, ctx)
  214.             return binding
  215.  
  216.         def _convert_list(binding):
  217.             if isinstance(binding, list):
  218.                 binding = annotate.MethodBinding(
  219.                     name, annotate.Method(arguments=[
  220.                     annotate.Argument(n, v, v.id)
  221.                     for (n, v) in binding]))
  222.             return binding
  223.  
  224.         binding = util.maybeDeferred(getattr, self, 'bind_%s' % name)
  225.         return binding.addCallback(_get_binding).addCallback(_convert_list)
  226.  
  227.     def getDefault(self, forBinding):
  228.         """Get a default value for a given binding. If the
  229.         binding is a Property, get the current value of
  230.         that property off self. If not, simply return
  231.         forBinding.default.
  232.         """
  233.         ## If it is a Property, get the value off self
  234.         if not isinstance(forBinding, annotate.Argument):
  235.             if hasattr(self, forBinding.name):
  236.                 return getattr(self, forBinding.name)
  237.         return forBinding.default
  238.  
  239.     def postForm(self, ctx, bindingName, args):
  240.         """Accept a form post to the given bindingName.
  241.         The post arguments are given in args.
  242.  
  243.         This will invoke the IInputProcessor for the
  244.         binding with the given name. If it succeeds, the
  245.         property will be modified or the method will have
  246.         been called. If it fails, a ValidateError exception
  247.         will be raised.
  248.         """
  249.         def _callback(binding):
  250.             ctx.remember(binding, iformless.IBinding)
  251.             ctx.remember(self, iformless.IConfigurable)
  252.             rv = iformless.IInputProcessor(binding).process(ctx, self, args)
  253.             ctx.remember(rv, inevow.IHand)
  254.             ctx.remember('%r success.' % bindingName, inevow.IStatusMessage)
  255.             return rv
  256.         return util.maybeDeferred(self.getBinding, ctx, 
  257.                                   bindingName).addCallback(_callback)
  258.  
  259.  
  260. class ConfigurableFactory:
  261.     """Locates configurables by looking for methods that start with
  262.     configurable_ and end with the name of the configurable. The method
  263.     should take a single arg (other than self) - the current context.
  264.     """
  265.     implements(iformless.IConfigurableFactory)
  266.  
  267.     def locateConfigurable(self, context, name):
  268.         """formless.webform.renderForms calls locateConfigurable on the IConfigurableFactory
  269.         instance it retrieves from the context. It passes the "name" that was passed to it,
  270.         so if renderForms() was placed in the DOM, locateConfigurable will be called with
  271.         name = ''; if renderForms('foo') was placed in the DOM, locateConfigurable will
  272.         be called with name = 'foo'.
  273.  
  274.         This default implementation of locateConfigurable looks for a configurable_* method
  275.         corresponding to the name which was passed.
  276.         """
  277.         return util.maybeDeferred(getattr(self, 'configurable_%s'%name),
  278.                                   context).addCallback(iformless.IConfigurable)
  279.  
  280.     def configurable_(self, context):
  281.         """Configurable factory for use when self is a configurable;
  282.         aka it implements IConfigurable or one or more TypedInterface
  283.         subclasses. Usage:
  284.  
  285.         >>> class IFoo(TypedInterface):
  286.         ...     def bar(): pass
  287.         ...     bar = autocallable(bar)
  288.         ...
  289.         >>> class Foo(Page):
  290.         ...     implements(IFoo)
  291.         ...
  292.         ...     def bar():
  293.         ...         print "bar called through the web!"
  294.         ...
  295.         ...     def render_forms(self, ctx, data):
  296.         ...         return renderForms() # or renderForms('')
  297.         ...
  298.         ...     docFactory = stan(render_forms).
  299.         """
  300.         if filter(lambda x: issubclass(x, annotate.TypedInterface), providedBy(self)):
  301.             warnings.warn("[0.5] Subclassing TypedInterface to declare annotations is deprecated. Please provide bind_* methods on your Page or Fragment subclass instead.", DeprecationWarning)
  302.             from formless import configurable
  303.             return configurable.TypedInterfaceConfigurable(self)
  304.         return self
  305.  
  306.     def configurable_original(self, ctx):
  307.         """Configurable factory for use when self.original is a configurable;
  308.         aka it implements IConfigurable or one or more TypedInterface
  309.         subclasses. Usage:
  310.  
  311.  
  312.         >>> class Foo(Page):
  313.         ...     def __init__(self):
  314.         ...         self.original = SomeConfigurable()
  315.         ...     def render_forms(self, ctx, data):
  316.         ...         return renderForms('original')
  317.         ...     docFactory = stan(render_forms)
  318.         """
  319.         return self.original
  320.  
  321. _CARRYOVER = {}
  322.  
  323.  
  324. def defaultsFactory(ctx):
  325.     co = _CARRYOVER.get(
  326.         ctx.tag.args.get('_nevow_carryover_', [None])[0], None)
  327.     from formless import webform
  328.     defaults = webform.FormDefaults()
  329.     if co is not None:
  330.         e = iformless.IFormErrors(co, {})
  331.         for k, v in e.items():
  332.             defaults.getAllDefaults(k).update(v.partialForm)
  333.     return defaults
  334.  
  335.  
  336. def errorsFactory(ctx):
  337.     co = _CARRYOVER.get(
  338.         ctx.tag.args.get('_nevow_carryover_', [None])[0], None)
  339.     from formless import webform
  340.     errs = webform.FormErrors()
  341.     if co is not None:
  342.         e = iformless.IFormErrors(co, {})
  343.         for k, v in e.items():
  344.             errs.updateErrors(k, v.errors)
  345.             errs.setError(k, v.formErrorMessage)
  346.     return errs
  347.  
  348.  
  349. def handFactory(ctx):
  350.     co = _CARRYOVER.get(
  351.         ctx.tag.args.get('_nevow_carryover_', [None])[0], None)
  352.     return inevow.IHand(co, None)
  353.  
  354.  
  355. def statusFactory(ctx):
  356.     co = _CARRYOVER.get(
  357.         ctx.tag.args.get('_nevow_carryover_', [None])[0], None)
  358.     return inevow.IStatusMessage(co, None)
  359.  
  360.  
  361. def originalFactory(ctx):
  362.     return ctx.tag
  363.  
  364.  
  365. class Fragment(DataFactory, RenderFactory, MacroFactory, ConfigurableMixin):
  366.     """A fragment is a renderer that can be embedded in a stan document and
  367.     hooks its template (from the docFactory) up to its data_ and render_
  368.     methods, i.e. it remembers itself as the IRendererFactory and IContainer.
  369.  
  370.     Fragment primarily serves as the base for Page, Nevow's web resource, but
  371.     it can be used for more complex rendering. For instance, a fragment might
  372.     be used to encapsulate the rendering of a complex piece of data where the
  373.     template is read from disk and contains standard renderers (sequence,
  374.     mapping etc) and/or custom render methods.
  375.     """
  376.     implements(inevow.IRenderer, inevow.IGettable)
  377.  
  378.     docFactory = None
  379.     original = None
  380.  
  381.     def __init__(self, original=None, docFactory=None):
  382.         if original is not None:
  383.             self.original = original
  384.         self.toremember = []
  385.         if docFactory is not None:
  386.             self.docFactory = docFactory
  387.  
  388.     def get(self, context):
  389.         return self.original
  390.  
  391.     def rend(self, context, data):
  392.         # Create a new context so the current context is not polluted with
  393.         # remembrances.
  394.         context = WovenContext(parent=context)
  395.  
  396.         # Remember me as lots of things
  397.         self.rememberStuff(context)
  398.  
  399.         preprocessors = _getPreprocessors(self)
  400.  
  401.         # This tidbit is to enable us to include Page objects inside
  402.         # stan expressions and render_* methods and the like. But
  403.         # because of the way objects can get intertwined, we shouldn't
  404.         # leave the pattern changed.
  405.         old = self.docFactory.pattern
  406.         self.docFactory.pattern = 'content'
  407.         self.docFactory.precompiledDoc = None
  408.         try:
  409.             try:
  410.                 doc = self.docFactory.load(context, preprocessors)
  411.             finally:
  412.                 self.docFactory.pattern = old
  413.                 self.docFactory.precompiledDoc = None
  414.         except TypeError, e:
  415.             # Avert your eyes now! I don't want to catch anything but IQ
  416.             # adaption exceptions here but all I get is TypeError. This whole
  417.             # section of code is a complete hack anyway so one more won't
  418.             # matter until it's all removed. ;-).
  419.             if 'nevow.inevow.IQ' not in str(e):
  420.                 raise
  421.             doc = self.docFactory.load(context, preprocessors)
  422.         except NodeNotFound:
  423.             doc = self.docFactory.load(context, preprocessors)
  424.         else:
  425.             if old == 'content':
  426.                 warnings.warn(
  427.                     """[v0.5] Using a Page with a 'content' pattern is
  428.                                deprecated.""",
  429.                     DeprecationWarning,
  430.                     stacklevel=2)
  431.  
  432.         context.tag = tags.invisible[doc]
  433.         return context
  434.  
  435.     def remember(self, obj, inter=None):
  436.         """Remember an object for an interface on new PageContexts which are
  437.         constructed around this Page. Whenever this Page is involved in object
  438.         traversal in the future, all objects will be visible to .locate() calls
  439.         at the level of a PageContext wrapped around this Page and all contexts
  440.         below it.
  441.  
  442.         This does not affect existing Context instances.
  443.         """
  444.         self.toremember.append((obj, inter))
  445.  
  446.     def rememberStuff(self, ctx):
  447.         ctx.remember(self, inevow.IRenderer)
  448.         ctx.remember(self, inevow.IRendererFactory)
  449.         ctx.remember(self, inevow.IMacroFactory)
  450.         ctx.remember(self, inevow.IData)
  451.  
  452.  
  453. class ChildLookupMixin(FreeformChildMixin):
  454.     ##
  455.     # IResource methods
  456.     ##
  457.  
  458.     children = None
  459.     def locateChild(self, ctx, segments):
  460.         """Locate a child page of this one. ctx is a
  461.         nevow.context.PageContext representing the parent Page, and segments
  462.         is a tuple of each element in the URI. An tuple (page, segments) should be
  463.         returned, where page is an instance of nevow.rend.Page and segments a tuple
  464.         representing the remaining segments of the URI. If the child is not found, return
  465.         NotFound instead.
  466.  
  467.         locateChild is designed to be easily overridden to perform fancy lookup tricks.
  468.         However, the default locateChild is useful, and looks for children in three places,
  469.         in this order:
  470.  
  471.          - in a dictionary, self.children
  472.          - a member of self named child_<childname>. This can be either an
  473.            attribute or a method. If an attribute, it should be an object which
  474.            can be adapted to IResource. If a method, it should take the context
  475.            and return an object which can be adapted to IResource.
  476.          - by calling self.childFactory(ctx, name). Name is a single string instead
  477.            of a tuple of strings. This should return an object that can be adapted
  478.            to IResource.
  479.         """
  480.  
  481.         if self.children is not None:
  482.             r = self.children.get(segments[0], None)
  483.             if r is not None:
  484.                 return r, segments[1:]
  485.  
  486.         w = getattr(self, 'child_%s'%segments[0], None)
  487.         if w is not None:
  488.             if inevow.IResource(w, None) is not None:
  489.                 return w, segments[1:]
  490.             r = w(ctx)
  491.             if r is not None:
  492.                 return r, segments[1:]
  493.  
  494.         r = self.childFactory(ctx, segments[0])
  495.         if r is not None:
  496.             return r, segments[1:]
  497.  
  498.         return FreeformChildMixin.locateChild(self, ctx, segments)
  499.  
  500.     def childFactory(self, ctx, name):
  501.         """Used by locateChild to return children which are generated
  502.         dynamically. Note that higher level interfaces use only locateChild,
  503.         and only nevow.rend.Page.locateChild uses this.
  504.  
  505.         segment is a string representing one element of the URI. Request is a
  506.         nevow.appserver.NevowRequest.
  507.  
  508.         The default implementation of this always returns None; it is intended
  509.         to be overridden."""
  510.         return None
  511.  
  512.     def putChild(self, name, child):
  513.         if self.children is None:
  514.             self.children = {}
  515.         self.children[name] = child
  516.  
  517.  
  518. class Page(Fragment, ConfigurableFactory, ChildLookupMixin):
  519.     """A page is the main Nevow resource and renders a document loaded
  520.     via the document factory (docFactory).
  521.     """
  522.  
  523.     implements(inevow.IResource)
  524.  
  525.     buffered = False
  526.  
  527.     beforeRender = None
  528.     afterRender = None
  529.     addSlash = None
  530.  
  531.     flattenFactory = lambda self, *args: flat.flattenFactory(*args)
  532.  
  533.     def renderHTTP(self, ctx):
  534.         if self.beforeRender is not None:
  535.             return util.maybeDeferred(self.beforeRender,ctx).addCallback(
  536.                     lambda result,ctx: self._renderHTTP(ctx),ctx)
  537.         return self._renderHTTP(ctx)
  538.  
  539.     def _renderHTTP(self, ctx):
  540.         request = inevow.IRequest(ctx)
  541.         ## XXX request is really ctx now, change the name here
  542.         if self.addSlash and inevow.ICurrentSegments(ctx)[-1] != '':
  543.             request.redirect(request.URLPath().child(''))
  544.             return ''
  545.  
  546.         log.msg(http_render=None, uri=request.uri)
  547.  
  548.         self.rememberStuff(ctx)
  549.  
  550.         def finishRequest():
  551.             carryover = request.args.get('_nevow_carryover_', [None])[0]
  552.             if carryover is not None and _CARRYOVER.has_key(carryover):
  553.                 del _CARRYOVER[carryover]
  554.             if self.afterRender is not None:
  555.                 return util.maybeDeferred(self.afterRender,ctx)
  556.  
  557.         if self.buffered:
  558.             io = StringIO()
  559.             writer = io.write
  560.             def finisher(result):
  561.                 request.write(io.getvalue())
  562.                 return util.maybeDeferred(finishRequest).addCallback(lambda r: result)
  563.         else:
  564.             writer = request.write
  565.             def finisher(result):
  566.                 return util.maybeDeferred(finishRequest).addCallback(lambda r: result)
  567.  
  568.         preprocessors = _getPreprocessors(self)
  569.         doc = self.docFactory.load(ctx, preprocessors)
  570.         ctx =  WovenContext(ctx, tags.invisible[doc])
  571.  
  572.         return self.flattenFactory(doc, ctx, writer, finisher)
  573.  
  574.     def rememberStuff(self, ctx):
  575.         Fragment.rememberStuff(self, ctx)
  576.         ctx.remember(self, inevow.IResource)
  577.  
  578.     def renderString(self, ctx=None):
  579.         """Render this page outside of the context of a web request, returning
  580.         a Deferred which will result in a string.
  581.  
  582.         If twisted is not installed, this method will return a string result immediately,
  583.         and this method is equivalent to renderSynchronously.
  584.         """
  585.         io = StringIO()
  586.         writer = io.write
  587.  
  588.         def finisher(result):
  589.             return io.getvalue()
  590.  
  591.         ctx = PageContext(parent=ctx, tag=self)
  592.         self.rememberStuff(ctx)
  593.         doc = self.docFactory.load(ctx)
  594.         ctx =  WovenContext(ctx, tags.invisible[doc])
  595.  
  596.         return self.flattenFactory(doc, ctx, writer, finisher)
  597.  
  598.     def renderSynchronously(self, ctx=None):
  599.         """Render this page synchronously, returning a string result immediately.
  600.         Raise an exception if a Deferred is required to complete the rendering
  601.         process.
  602.         """
  603.         io = StringIO()
  604.  
  605.         ctx = PageContext(parent=ctx, tag=self)
  606.         self.rememberStuff(ctx)
  607.         doc = self.docFactory.load(ctx)
  608.         ctx =  WovenContext(ctx, tags.invisible[doc])
  609.  
  610.         def raiseAlways(item):
  611.             raise NotImplementedError("renderSynchronously can not support"
  612.                 " rendering: %s" % (item, ))
  613.  
  614.         list(flat.iterflatten(doc, ctx, io.write, raiseAlways))
  615.  
  616.         return io.getvalue()
  617.  
  618.     def child_(self, ctx):
  619.         """When addSlash is True, a page rendered at a url with no
  620.         trailing slash and a page rendered at a url with a trailing
  621.         slash will be identical. addSlash is useful for the root
  622.         resource of a site or directory-like resources.
  623.         """
  624.         # Only allow an empty child, by default, if it's on the end
  625.         # and we're a directoryish resource (addSlash = True)
  626.         if self.addSlash and len(inevow.IRemainingSegments(ctx)) == 1:
  627.             return self
  628.         return None
  629.  
  630.     def webFormPost(self, request, res, configurable, ctx, bindingName, args):
  631.         """Accept a web form post, either redisplaying the original form (with
  632.         errors) if validation fails, or redirecting to the appropriate location after
  633.         the post succeeds. This hook exists specifically for formless.
  634.  
  635.         New in 0.5, _nevow_carryover_ is only used if an autocallable method
  636.         returns a result that needs to be carried over.
  637.  
  638.         New in 0.5, autocallables may return a nevow.url.URL or URLOverlay
  639.         instance rather than setting IRedirectAfterPost on the request.
  640.  
  641.         New in 0.5, autocallables may return a Page instance to have that Page
  642.         instance rendered at the post target URL with no redirects at all. Useful
  643.         for multi-step wizards.
  644.         """
  645.         def redirectAfterPost(aspects):
  646.             hand = aspects.get(inevow.IHand)
  647.             refpath = None
  648.             if hand is not None:
  649.                 if isinstance(hand, Page):
  650.                     refpath = url.here
  651.                     if 'freeform_hand' not in inevow.IRequest(ctx).prepath:
  652.                         refpath = refpath.child('freeform_hand')
  653.                 if isinstance(hand, (url.URL, url.URLOverlay)):
  654.                     refpath, hand = hand, None
  655.  
  656.             if refpath is None:
  657.                 redirectAfterPost = request.getComponent(iformless.IRedirectAfterPost, None)
  658.                 if redirectAfterPost is None:
  659.                     ref = request.getHeader('referer')
  660.                     if ref:
  661.                         refpath = url.URL.fromString(ref)
  662.                     else:
  663.                         refpath = url.here
  664.                 else:
  665.                     warnings.warn("[0.5] IRedirectAfterPost is deprecated. Return a URL instance from your autocallable instead.", DeprecationWarning, 2)
  666.                     ## Use the redirectAfterPost url
  667.                     ref = str(redirectAfterPost)
  668.                     refpath = url.URL.fromString(ref)
  669.  
  670.             if hand is not None or aspects.get(iformless.IFormErrors) is not None:
  671.                 magicCookie = '%s%s%s' % (now(),request.getClientIP(),random.random())
  672.                 refpath = refpath.replace('_nevow_carryover_', magicCookie)
  673.                 _CARRYOVER[magicCookie] = C = tpc.Componentized()
  674.                 for k, v in aspects.iteritems():
  675.                     C.setComponent(k, v)
  676.  
  677.             destination = flat.flatten(refpath, ctx)
  678.             request.redirect(destination)
  679.             from nevow import static
  680.             return static.Data('You posted a form to %s' % bindingName, 'text/plain'), ()
  681.  
  682.         return util.maybeDeferred(
  683.             configurable.postForm, ctx, bindingName, args
  684.         ).addCallback(
  685.             self.onPostSuccess, request, ctx, bindingName, redirectAfterPost
  686.         ).addErrback(
  687.             self.onPostFailure, request, ctx, bindingName, redirectAfterPost
  688.         )
  689.  
  690.     def onPostSuccess(self, result, request, ctx, bindingName, redirectAfterPost):
  691.         if result is None:
  692.             message = "%s success." % formless.nameToLabel(bindingName)
  693.         else:
  694.             message = result
  695.  
  696.         return redirectAfterPost({inevow.IHand: result, inevow.IStatusMessage: message})
  697.  
  698.     def onPostFailure(self, reason, request, ctx, bindingName, redirectAfterPost):
  699.         reason.trap(formless.ValidateError)
  700.         return redirectAfterPost({iformless.IFormErrors: {bindingName: reason.value}})
  701.  
  702.  
  703. def sequence(context, data):
  704.     """Renders each item in the sequence using patterns found in the
  705.     children of the element.
  706.  
  707.     Sequence recognises the following patterns:
  708.  
  709.      - header: Rendered at the start, before the first item. If multiple
  710.        header patterns are provided they are rendered together in the
  711.        order they were defined.
  712.  
  713.      - footer: Just like the header only renderer at the end, after the
  714.        last item.
  715.  
  716.      - item: Rendered once for each item in the sequence. If multiple
  717.        item patterns are provided then the pattern is cycled in the
  718.        order defined.
  719.  
  720.      - divider: Rendered once between each item in the sequence. Multiple
  721.        divider patterns are cycled.
  722.  
  723.      - empty: Rendered instead of item and divider patterns when the
  724.        sequence contains no items.
  725.  
  726.     Example::
  727.  
  728.      <table nevow:render="sequence" nevow:data="peopleSeq">
  729.        <tr nevow:pattern="header">
  730.          <th>name</th>
  731.          <th>email</th>
  732.        </tr>
  733.        <tr nevow:pattern="item" class="odd">
  734.          <td>name goes here</td>
  735.          <td>email goes here</td>
  736.        </tr>
  737.        <tr nevow:pattern="item" class="even">
  738.          <td>name goes here</td>
  739.          <td>email goes here</td>
  740.        </tr>
  741.        <tr nevow:pattern="empty">
  742.          <td colspan="2"><em>they've all gone!</em></td>
  743.        </tr>
  744.      </table>
  745.  
  746.     """
  747.     tag = context.tag
  748.     headers = tag.allPatterns('header')
  749.     pattern = tag.patternGenerator('item')
  750.     divider = tag.patternGenerator('divider', default=tags.invisible)
  751.     content = [(pattern(data=element), divider(data=element)) for element in data]
  752.     if not content:
  753.         content = tag.allPatterns('empty')
  754.     else:
  755.         ## No divider after the last thing.
  756.         content[-1] = content[-1][0]
  757.     footers = tag.allPatterns('footer')
  758.  
  759.     return tag.clear()[ headers, content, footers ]
  760.  
  761.  
  762. def mapping(context, data):
  763.     """Fills any slots in the element's children with data from a
  764.     dictionary. The dict keys are used as the slot names, the dict
  765.     values are used as filling.
  766.  
  767.     Example::
  768.  
  769.      <tr nevow:render="mapping" nevow:data="personDict">
  770.        <td><nevow:slot name="name"/></td>
  771.        <td><nevow:slot name="email"/></td>
  772.      </tr>
  773.     """
  774.     for k, v in data.items():
  775.         context.fillSlots(k, v)
  776.     return context.tag
  777.  
  778.  
  779. def string(context, data):
  780.     return context.tag.clear()[str(data)]
  781.  
  782.  
  783. def data(context, data):
  784.     """Replace the tag's content with the current data.
  785.     """
  786.     return context.tag.clear()[data]
  787.  
  788.  
  789. class FourOhFour:
  790.     """A simple 404 (not found) page.
  791.     """
  792.     implements(inevow.IResource)
  793.  
  794.     notFound = "<html><head><title>Page Not Found</title></head><body>Sorry, but I couldn't find the object you requested.</body></html>"
  795.     original = None
  796.  
  797.     def locateChild(self, ctx, segments):
  798.         return NotFound
  799.  
  800.     def renderHTTP(self, ctx):
  801.         inevow.IRequest(ctx).setResponseCode(404)
  802.         # Look for an application-remembered handler
  803.         try:
  804.             notFoundHandler = ctx.locate(inevow.ICanHandleNotFound)
  805.         except KeyError, e:
  806.             return self.notFound
  807.         # Call the application-remembered handler but if there are any errors
  808.         # then log it and fallback to the standard message.
  809.         try:
  810.             return notFoundHandler.renderHTTP_notFound(PageContext(parent=ctx, tag=notFoundHandler))
  811.         except:
  812.             log.err()
  813.             return self.notFound
  814.  
  815.     def __nonzero__(self):
  816.         return False
  817.  
  818.  
  819. # Not found singleton
  820. NotFound = None, ()
  821.